package com.cy;

import com.cy.interceptor.EventLogInterceptor;
import com.cy.shiro.ShiroUser;
import com.cy.utils.SimpleSqlUtils;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.Model;
import com.jfinal.plugin.activerecord.Page;
import com.jfinal.plugin.activerecord.TableInfo;
import com.jfinal.plugin.activerecord.TableInfoMapping;
import com.jfinal.plugin.ehcache.CacheKit;
import jodd.typeconverter.Convert;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.apache.log4j.MDC;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.jsoup.Jsoup;
import org.jsoup.safety.Whitelist;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;

@SuppressWarnings("unchecked")
public abstract class BasicModel<M extends BasicModel> extends Model<M> {
    private static final long serialVersionUID = -8453134995045070294L;

    private static final TableInfoMapping tableInfoMapping = TableInfoMapping.me();
    protected static Logger logger;

    /*不同平台资源显示权限*/
    public static final int CLMP = 1;
    public static final int CCRM = 2;

    public String getTableName() {
        return tableInfoMapping.getTableInfo(getClass()).getTableName();
    }

    public String getCacheName() {
        return getTableName() + "_Cache";
    }

    public String getPkName() {
        return tableInfoMapping.getTableInfo(getClass()).getPrimaryKey();
    }

    public List<M> findAll() {
        return find("SELECT * FROM " + getTableName());
    }

    public Page<M> paginate(Integer pageNumber, Integer pageSize, String column, M model) {
        List<Object> paras = new ArrayList<>();
        StringBuffer sqlExceptSelectBuffer = new StringBuffer("FROM " + getTableName());
        StringBuffer selectBuffer = new StringBuffer("SELECT ").append(column);

        pageNumber = pageNumber == null || pageNumber == 0 ? 1 : ++pageNumber;
        pageSize = pageSize == null || pageSize == 0 ? 20 : pageSize;

        if (model != null) {
            int i = 0;
            String[] attrNames = model.getAttrNames();
            for (String attrName : attrNames) {
                Object attrValue = model.get(attrName);
                if (attrValue != null) {
                    sqlExceptSelectBuffer.append(i++ == 0 ? " WHERE " : " AND ");
                    sqlExceptSelectBuffer.append("`" + attrName + "`=?");
                    paras.add(attrValue);
                }
            }
        }

        return paras.size() > 0 ? paginate(pageNumber, pageSize, selectBuffer.toString(), sqlExceptSelectBuffer.toString(), paras.toArray())
                                : paginate(pageNumber, pageSize, selectBuffer.toString(), sqlExceptSelectBuffer.toString());
    }

    /**
     * 覆盖,一般用于更新.temp的值会覆盖原有的值
     * 如果是要删除某个值.merge后请重新赋值.
     *
     * @param temp
     * @return 更新日志.
     */
    public List<String> cover(M temp) {
        return cover(temp.getAttrs());

    }

    /**
     * 覆盖,一般用于更新.temp的值会覆盖原有的值
     * 如果是要删除某个值.merge后请重新赋值.
     *
     * @param temp
     * @return 更新日志.
     */
    public List<String> cover(Map<String, Object> temp) {
        List<String> changeLog = new ArrayList<>();
        for (String attrName : temp.keySet()) {
            if (getPkName().equals(attrName)) {
                continue;
            }
            Object value = temp.get(attrName);
            if (value == null) {
                value = "";
            }
            if (tableInfoMapping.getTableInfo(getClass()).hasColumnLabel(attrName) && !value.equals(get(attrName))) {
                set(attrName, value);
                changeLog.add(attrName + " : " + get(attrName) + " to " + value);
            }
        }
        return changeLog;
    }

    /**
     * 覆盖
     *
     * @param includedAttrs 包含属性
     * @return 更新日志.
     */
    public List<String> coverInclude(M temp, Set<String> includedAttrs) {
        return coverInclude(temp.getAttrs(), includedAttrs);

    }

    /**
     * 覆盖
     *
     * @param includedAttrs 包含属性
     * @return 更新日志.
     */
    public List<String> coverInclude(Map<String, Object> temp, Set<String> includedAttrs) {
        List<String> changeLog = new ArrayList<>(temp.size());
        if (includedAttrs != null && includedAttrs.size() > 0){
            for (String attrName : temp.keySet()) {
                if (getPkName().equals(attrName)) {
                    continue;
                }
                if (!includedAttrs.contains(attrName)){
                    continue;
                }
                Object value = temp.get(attrName);
                if (tableInfoMapping.getTableInfo(getClass()).hasColumnLabel(attrName)) {
                    set(attrName, value);
                    changeLog.add(attrName + " : " + get(attrName) + " to " + value);
                }
            }
        }
        return changeLog;
    }

    /**
     * 覆盖
     *
     * @param excludedAttrs 排除属性
     * @return 更新日志.
     */
    public List<String> coverExclude(M temp, Set<String> excludedAttrs) {
        return coverExclude(temp.getAttrs(), excludedAttrs);

    }

    /**
     * 覆盖
     *
     * @param excludedAttrs 排除属性
     * @return 更新日志.
     */
    public List<String> coverExclude(Map<String, Object> temp, Set<String> excludedAttrs) {
        List<String> changeLog = new ArrayList<>(temp.size());
        for (String attrName : temp.keySet()) {
            if (getPkName().equals(attrName) || (excludedAttrs != null && excludedAttrs.size() > 0 && excludedAttrs.contains(attrName))) {
                continue;
            }
            Object value = temp.get(attrName);
            if (tableInfoMapping.getTableInfo(getClass()).hasColumnLabel(attrName)) {
                set(attrName, value);
                changeLog.add(attrName + " : " + get(attrName) + " to " + value);
            }
        }
        return changeLog;
    }

    /**
     * 覆盖,一般用于更新.temp的值会覆盖原有的值
     * 如果是要删除某个值.merge后请重新赋值.
     *
     * @param temp
     * @return 更新日志.
     */
    public List<String> coverNotNull(M temp) {
        return coverNotNull(temp.getAttrs());
    }

    /**
     * 覆盖,一般用于更新.temp的值会覆盖原有的值
     * 如果是要删除某个值.merge后请重新赋值.
     * 一般处理某一状态值
     *
     * @param temp
     * @return 更新日志.
     */
    public List<String> coverNotNull(Map<String, Object> temp) {
        List<String> changeLog = new ArrayList<>();
        for (String attrName : temp.keySet()) {
            if (getPkName().equals(attrName)) {
                continue;
            }
            Object value = temp.get(attrName);
            if (value != null && StringUtils.isNotBlank(value.toString()) && tableInfoMapping.getTableInfo(getClass()).hasColumnLabel(attrName) && !value.equals(get(attrName))) {
                set(attrName, value);
                changeLog.add(attrName + " : " + get(attrName) + " to " + value);
            }
        }
        return changeLog;

    }

    /**
     * 将另外的Model的值PUT进去合并起来,
     * PUT和SET有区别.
     *
     * @param map
     */
    public void merge(Map<String, Object> map) {

        for (String s : map.keySet()) {
            Object value = map.get(s);
            if (value != null && !value.equals("")) {
                put(s, value);
            }
        }
    }


    protected Logger getLog() {
        String startTimeStr = (String)MDC.get("_startTime");
        if (StringUtils.isNotBlank(startTimeStr)){
            long startTime = Long.parseLong((String) MDC.get("_startTime"));
            MDC.put("costTime", String.valueOf(System.currentTimeMillis() - startTime));
        }

        Subject subject = SecurityUtils.getSubject();
        final ShiroUser shiroUser = (ShiroUser) subject.getPrincipal();
        if(null != shiroUser){
            if(shiroUser.getType() == 1){
                MDC.put("type", 0);
            }else if(shiroUser.getType() == 0){
                MDC.put("type", 1);
            }
        }

        return logger;
    }

    /**
     * CUD 操作重写加上日志系统
     *
     * @return
     */

    @Override
    public boolean save() {
        long startTime = System.currentTimeMillis();
        boolean isSave = super.save();    //To change body of overridden methods use File | Settings | File Templates.
        long costTime = System.currentTimeMillis() - startTime;
        if (isSave) {
            MDC.put("costTime", String.valueOf(costTime));
            getLog().trace("insert@[" + getTableName() + "]  values:" + toString());
        }
        return isSave;
    }

    @Override
    public boolean update() {
        long startTime = System.currentTimeMillis();
        boolean isUpdate = super.update();    //To change body of overridden methods use File | Settings | File Templates.
        long costTime = System.currentTimeMillis() - startTime;
        if (isUpdate) {
            CacheKit.remove(getCacheName(), get(getPkName()));
            MDC.put("costTime", String.valueOf(costTime));
            getLog().trace("update@[" + getTableName() + "]  values:" + toString());
        }
        return isUpdate;
    }

    @Override
    public boolean deleteById(Object id) {
        long startTime = System.currentTimeMillis();
        boolean isDelete = super.deleteById(id);    //To change body of overridden methods use File | Settings | File Templates.
        long costTime = System.currentTimeMillis() - startTime;
        if (isDelete) {
            CacheKit.remove(getCacheName(), id);
            MDC.put("costTime", String.valueOf(costTime));
            getLog().trace("delete@[" + getTableName() + "]  values:" + id);
        }
        return isDelete;
    }

    public boolean deleteBatch(String column, Object[] values){
        long startTime = System.currentTimeMillis();
        TableInfo tInfo = tableInfoMapping.getTableInfo(getClass());
        StringBuilder sql = new StringBuilder("DELETE FROM ");
        sql.append(tInfo.getTableName());
        sql.append(" WHERE ");
        sql.append(SimpleSqlUtils.in(column, values));
        boolean isDelete = Db.update(sql.toString()) > 0;
        long costTime = System.currentTimeMillis() - startTime;
        if (isDelete) {
            MDC.put("costTime", String.valueOf(costTime));
            getLog().trace("delete@[" + getTableName() + "]  values:" + Arrays.toString(values));
        }
        return isDelete;
    }

    public boolean deleteBatch(String column, Object value){
        return deleteBatch(column, new Object[]{value});
    }

    @Override
    public boolean delete() {
        long startTime = System.currentTimeMillis();
        boolean isDelete = super.delete();    //To change body of overridden methods use File | Settings | File Templates.
        long costTime = System.currentTimeMillis() - startTime;
        if (isDelete) {
            CacheKit.remove(getCacheName(), get(getPkName()));
            MDC.put("costTime", String.valueOf(costTime));
            getLog().trace("delete@[" + getTableName() + "]  values:" + getAttrs().get(getPkName()));
        }
        return isDelete;
    }

    @Override
    public M set(String attr, Object value) {
        attr = Jsoup.clean(attr, Whitelist.simpleText());
        return super.set(attr, value);
    }

    @Override
    public Long getLong(String attr) {
        if (null == get(attr)) {
            return 0L;
        }
        return Convert.toLong(get(attr));
    }

    /**
     * 不过滤
     *
     * @param attr
     * @param value
     * @return
     */
    public M setUnSafe(String attr, Object value) {
        return super.set(attr, value);
    }

    /**
     * 初始化日志记录
     */
    public static void initLogger() {
        logger = LogManager.getLogger(EventLogInterceptor.EventLogName);
    }

}